package com.lagou.config;

import com.lagou.domain.Permission;
import com.lagou.filter.ValidateCodeFilter;
import com.lagou.handle.MyAccessDeniedHandler;
import com.lagou.service.PermissionService;
import com.lagou.service.impl.MyAuthenticationService;
import com.lagou.service.impl.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.sql.DataSource;
import java.util.List;

/**
 * 项目名: lagou-security-management
 * 文件名: SecurityConfiguration
 * 创建者: LiJun Wen
 * 创建时间: 2021/6/22 13:06
 * 描述: SpringSecurity配置类
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启注解支持  对方法注解的支持
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    MyUserDetailsService myUserDetailsService;
    @Autowired
    MyAuthenticationService myAuthenticationService;
    @Autowired
    ValidateCodeFilter validateCodeFilter;
    @Autowired
    MyAccessDeniedHandler myAccessDeniedHandler;
    @Autowired
    PermissionService permissionService;
    /**
     *
     * 身份安全管理器
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //解决静态资源被拦截的问题
        web.ignoring().antMatchers("/css/**", "/images/**", "/js/**",
                "/favicon.ico","/code/**");
    }

    /**
     * http请求处理方法
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*http.httpBasic()//开启httpbasic认证
                .and().authorizeRequests().
                        anyRequest().authenticated();//所有请求都需要登录认证才能访问*/
        //开启表单认证 授权所有请求都需要登录认证才能访问

        //加入用户名密码验证过滤器的前面
        http.addFilterBefore(validateCodeFilter,UsernamePasswordAuthenticationFilter.class);

        /*//基于url的权限控制
        //设置/user开头的请求需要ADMIN权限
        http.authorizeRequests().antMatchers("/user/**").hasRole("ADMIN");
        //设置/product开头的请求需要
        http.authorizeRequests().antMatchers("/product/**").access
                ("hasAnyRole('ADMIN','PRODUCT')and hasIpAddress('127.0.0.1')");*/

        //查询数据库所有权限列表
        List<Permission> list = permissionService.list();
        for (Permission permission : list) {
            //添加请求权限
            http.authorizeRequests().antMatchers(permission.getPermissionUrl())
                    .hasAnyAuthority(permission.getPermissionTag());
        }
        //设置权限不足的信息
        //设置没有权限执行的逻辑
        http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);
        http.formLogin()//开启表单认证
                .loginPage("/toLoginPage")//自定义登录页面
                .loginProcessingUrl("/login")//表单提交的路径
                .usernameParameter("username")//设置表单登录用户名
                .passwordParameter("password")//设置表单登录的密码
                .successForwardUrl("/")//设置登录成功后跳转的路径
                .successHandler(myAuthenticationService)//登录成功后的处理逻辑
                .failureHandler(myAuthenticationService)//登录失败后的处理逻辑
                .and().logout().logoutUrl("/logout")//退出后的访问路径
                .logoutSuccessHandler(myAuthenticationService)
                .and().rememberMe()//开启记住我功能
                .tokenValiditySeconds(1209600)//token失效时间  默认是1209600 两周
                .rememberMeParameter("remember-me")//自定义表单的input值 默认值也是
                .tokenRepository(getPersistentTokenRepository())
                .and().authorizeRequests()//授权请求
                .antMatchers("/toLoginPage").permitAll()//放行登录页面
                .anyRequest()//任何请求
                .authenticated();//认证
        //关闭csrf防护
        //http.csrf().disable();

        //开启csrf防护，定义哪些路径不需要防护
        http.csrf().ignoringAntMatchers("/user/saveOrUpdate");

        //加载同源域名下的iframe页面
        http.headers().frameOptions().sameOrigin();

        //设置session管理
        http.sessionManagement()
                .invalidSessionUrl("/toLoginPage")//session失效后跳转的路径
                .maximumSessions(1)//设置session的最大会话数量 互提
               // .maxSessionsPreventsLogin(false)//如果达到最大会话数量，就会阻止登录 false不进行生效 和expiredUrl冲突
                .expiredUrl("/toLoginPage")//session过期之后跳转的路径
         ;
        //开启跨域支持
        http.cors().configurationSource(corsConfigurationSource());


    }



    @Autowired
    DataSource dataSource;
    /**
     * 负责token与数据库之间的操作
     * @return
     */
    @Bean
    public PersistentTokenRepository getPersistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);//设置数据源
        //再次启动就要注释掉
        //tokenRepository.setCreateTableOnStartup(true);//启动时帮助我们自动创建一张表   存储token信息  第一次启动设置true  第二次false
        return tokenRepository;
    }

    /**
     * 配置跨域信息源
     */
    public CorsConfigurationSource corsConfigurationSource(){
        CorsConfiguration corsConfiguration=new CorsConfiguration();
        //允许跨域的站点
        corsConfiguration.addAllowedOrigin("*");
        //允许跨域的http方法
        corsConfiguration.addAllowedMethod("*");
        //允许跨域的请求头
        //corsConfiguration.addExposedHeader("*");
        corsConfiguration.addExposedHeader("Content-Type");
        corsConfiguration.addExposedHeader("X-Requested-With");
        corsConfiguration.addExposedHeader("accept");
        corsConfiguration.addExposedHeader("Origin");
        corsConfiguration.addExposedHeader( "Access-Control-Request-Method");
        corsConfiguration.addExposedHeader("Access-Control-Request-Headers");
        //允许带凭证
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        //对所有url都生效
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration);
        return urlBasedCorsConfigurationSource;
    }

}
